﻿/*
 * Copyright (c) 2009, Thiago M. Sayao <thiago.sayao@gmail.com>
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without 
 * modification, are permitted provided that the following conditions are met:
 *  * Redistributions of source code must retain the above copyright notice, 
 *    this list of conditions and the following disclaimer.
 *  * Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 *  * Neither the name of the author nor the names of its contributors may be 
 *    used to endorse or promote products derived from this software without 
 *    specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
 * POSSIBILITY OF SUCH DAMAGE.
 */

using System;
using System.Text;
using System.Text.RegularExpressions;
using System.Web;
using System.Globalization;
using System.Security.Cryptography;
using System.Collections.Generic;
using System.Linq;

namespace DotNetBurner.Core
{

    public static class StringExtensions
    {
        static readonly Regex MailRegex = new Regex(@"^([0-9a-zA-Z]+[-._+&])*[0-9a-zA-Z]+@([-0-9a-zA-Z]+[.])+[a-zA-Z]{2,6}$", RegexOptions.Singleline | RegexOptions.Compiled);
        static readonly Regex UrlRegex  = new Regex(@"(http|https)://([\w-]+\.)+[\w-]+(/[\w- ./?%&=]*)?", RegexOptions.Singleline | RegexOptions.Compiled);
 
        public static string RemoveNewLines(this string str)
        {
            /*
                 LF:    Line Feed, U+000A
                 CR:    Carriage Return, U+000D
                 CR+LF: CR followed by LF, U+000D followed by U+000A
                 NEL:   Next Line, U+0085
                 FF:    Form Feed, U+000C
                 LS:    Line Separator, U+2028
                 PS:    Paragraph Separator, U+2029
             */

            return str.Replace('\u000D', ' ').Replace('\u000A', ' ');
        }

        public static string NewLine2Br(this string str)
        {
            char lf = '\u000A';
            char cr = '\u000D';

            str = str.Replace(cr.ToString(), "");
            str = str.Replace(lf.ToString(), "<br />");

            return str;
        }

        public static string NewLine2Paragraph (this string str)
        {
            char cr = '\u000D';

            str = str.Replace (cr.ToString (), "");
            string[] lines = str.Split (new[] { '\u000A' }, StringSplitOptions.RemoveEmptyEntries);

            StringBuilder b = new StringBuilder();

            foreach (var line in lines)
            {
                b.Append("<p>{0}</p>".FormatWith(line));
            }

            return b.ToString ();
        }

        public static string ConvertToFileName(this string s)
        {
            StringBuilder stringBuilder = new StringBuilder();

            s = s.ToLowerInvariant ();

            for (int i = 0; i < s.Length; i++)
            {
                Char c = s[i];

                if (!Char.IsLetterOrDigit (c) && !Char.IsWhiteSpace (c))
                    continue;

                if (Char.IsWhiteSpace(c))
                    c = '_';

                stringBuilder.Append (c);
            }

            return stringBuilder.ToString();
        }

        public static string ConvertToUrlPath(this string target)
        {
            if (string.IsNullOrEmpty(target))
                return String.Empty;

            StringBuilder stringBuilder = new StringBuilder ();

            bool lastIsDash = false;

            for (int i = 0; i < target.Length; i++)
            {
                Char c = target[i];

                if (Char.IsLetterOrDigit(c))
                {
                    stringBuilder.Append(Char.ToLowerInvariant(c));
                    lastIsDash = false;
                }
                //dot not append '-' if: (1) it's the first char (2) if the previous char is '-', so we prevent '--'
                else if ((Char.IsWhiteSpace(c) || c == '-') && stringBuilder.Length > 0 && i > 0 && !lastIsDash)
                {
                    stringBuilder.Append('-');
                    lastIsDash = true;
                }
                else if (c == '#')
                {
                    stringBuilder.Append("sharp");
                }
            }

            return stringBuilder.ToString();
        }

        public static string RemoveDoubleHyphens(this string target)
        {
            while (target.Contains("--"))
            {
                target = target.Replace("--", "-");
            }

            return target;
        }

        public static string RemoveDoubleSpaces(this string target)
        {
            while (target.Contains("  "))
            {
                target = target.Replace("  ", " ");
            }

            return target;
        }

        public static string FormatWith(this string target, params object[] args)
        {
            return string.Format(target, args);
        }

        public static string WrapAt(this string target, int index)
        {
            if (target.Length < index)
                return target;

            string res = target.Substring(0, index);

            if (!res.EndsWith("...", true, CultureInfo.InvariantCulture))
                res = string.Concat(res, "...");

            return res;
        }

        public static bool IsValidTag(this string s)
        {
            if (s.Trim().Length == 0)
                return false;

            for (int i = 0; i < s.Length; i++)
            {
                Char c = s[i];

                if (!Char.IsLetterOrDigit(c) && !Char.IsWhiteSpace(c) && !Char.IsPunctuation(c))
                    return false;
            }

            return true;
        }

        public static bool IsValidUserName(this string s)
        {
            if (s.Trim().Length == 0)
                return false;

            for (int i = 0; i < s.Length; i++)
            {
                Char c = s[i];

                if (!Char.IsLetterOrDigit(c) && !(c == '.') && !(c == '_'))
                    return false;
            }

            return true;
        }

        public static bool IsValidEmail(this string email)
        {
            return MailRegex.IsMatch(email);
        }

        public static bool IsValidUrl(this string url)
        {
            return UrlRegex.IsMatch(url);
        }

        public static string ExtractDomainNameFromURL (this string url)
        {
            if (!url.Contains ("://"))
                url = String.Format("http://{0}", url);

            string domain = new Uri (url).Host;

            if(domain.StartsWith("www.", StringComparison.OrdinalIgnoreCase))
                domain = domain.Remove(0, 4);

            return domain;
        }

        public static string ExtractSiteFromURL(this string url)
        {
            if (!url.Contains ("://"))
                url = String.Format("http://{0}", url);

            Uri uri = new Uri (url);

            string site = String.Format("{0}://{1}", uri.Scheme, uri.Host);

            return site;
        }

        public static string GetURLHash(this string url)
        {
            string urlHash;

            using (var md5 = MD5.Create())
            {
                byte[] data = Encoding.Default.GetBytes(url.ToUpperInvariant());
                byte[] hash = md5.ComputeHash(data);

                urlHash = Convert.ToBase64String(hash);
            }

            return urlHash;
        }

        public static string EncryptMD5(this string value)
        {
            var md5 = new MD5CryptoServiceProvider();
            var valueArray = Encoding.ASCII.GetBytes(value);
            valueArray = md5.ComputeHash(valueArray);

            var encrypted = new StringBuilder ();

            for (int i = 0 ; i < valueArray.Length ; i++)
                encrypted.Append (valueArray[i].ToString ("x2").ToLower ());

            return encrypted.ToString ();
        }

        public static string UrlEncode(this string val)
        {
            val = HttpUtility.UrlEncode(val);

            return val;
        }

        public static string UrlDecode(this string val)
        {
            val = HttpUtility.UrlDecode(val);
            val = val.Replace(" ", "+");

            return val;
        }

        public static string HtmlEncode (this string str)
        {
            return HttpUtility.HtmlEncode (str);
        }

        public static string HtmlDecode (this string str)
        {
            return HttpUtility.HtmlDecode (str);
        }

        public static string AttributeEncode (this string target)
        {
            return HttpUtility.HtmlAttributeEncode (target);
        }

        public static string UTF8String(this string str, Encoding srcEncoding)
        {
            if (srcEncoding == Encoding.UTF8)
                return str;

            byte[] bytes = Encoding.Convert(srcEncoding, Encoding.UTF8, srcEncoding.GetBytes(str));

            return Encoding.UTF8.GetString(bytes);
        }

        public static string GetSummary(this string content, int minSize, int maxSize)
        {
            int len = content.Length;

            if (len == 0)
                return String.Empty;

            //maybe we are lucky and it is already summarized
            if (len <= maxSize)
                return content.RemoveDoubleSpaces().Trim();

            //go adding paragraphs until it's satisfied
            string res = GetSummaryByCriteria(content, minSize, maxSize, Environment.NewLine);

            if (res.Length != 0)
                return res.RemoveDoubleSpaces().Trim();

            //go adding dots until it's satisfied
            res = GetSummaryByCriteria(content, minSize, maxSize, ".");
            if (res.Length != 0)
                return res.RemoveDoubleSpaces().Trim();

            //crap, this was hard to satisfy.. just truncate it..
            return content.Substring(0, maxSize).RemoveDoubleSpaces().Trim();
        }

        static string GetSummaryByCriteria(string content, int minSize, int maxSize, string criteria)
        {
            int idx = content.IndexOf (criteria, minSize - 1, StringComparison.CurrentCultureIgnoreCase);

            while (idx != -1)
            {
                if (idx >= minSize && idx <= maxSize)
                    return content.Substring(0, idx + 1).Trim();

                idx = content.IndexOf (criteria, idx + 1, StringComparison.CurrentCultureIgnoreCase);
            }

            return String.Empty;
        }

        public static string[] FindKeyWords(this string text, string[] keywords, int top)
        {
            if (string.IsNullOrEmpty(text))
                return new string[0];

            Dictionary<string, int> matches = new Dictionary<string, int>(StringComparer.CurrentCultureIgnoreCase);

            foreach (string kword in keywords)
            {
                int lastmatchPos = 0;

                while (lastmatchPos != -1)
                {
                    lastmatchPos = text.IndexOf(kword, lastmatchPos + 1, StringComparison.CurrentCultureIgnoreCase);

                    if (lastmatchPos != -1)
                    {
                        if (matches.ContainsKey(kword))
                            matches[kword]++;
                        else
                            matches.Add(kword, 1);
                    }
                }
            }

            var res = (from m in matches
                       orderby m.Value descending, m.Key.Length descending
                       select m.Key).Take(top).ToArray();

            return res;
        }
    }
}